package com.ctshk.rpc.order.hotel.service.impl;
import com.ctshk.common.enums.hotel.HotelApiSupplier;
import com.ctshk.common.enums.hotel.HotelStarLevel;
import com.ctshk.rpc.hotel.dto.*;
import com.ctshk.rpc.hotel.req.NCNBHotelCreateOrderReq;
import com.ctshk.rpc.hotel.service.*;
import com.ctshk.rpc.user.service.IOrderEsService;
import com.google.common.collect.Lists;
import com.ctshk.rpc.order.hotel.dto.h5.HotelOrderContactPersonInfoDTO;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ctshk.common.constant.CurrencyRateExchangeConstant;
import com.ctshk.common.constant.PayNotifyUrlConstants;
import com.ctshk.common.dto.Result;
import com.ctshk.common.dto.payment.CallPayDTO;
import com.ctshk.common.enums.*;
import com.ctshk.common.enums.Currency;
import com.ctshk.common.enums.hotel.TouristType;
import com.ctshk.common.enums.payment.PayCurrency;
import com.ctshk.common.exception.BusinessException;
import com.ctshk.common.model.AppReq;
import com.ctshk.common.model.PageResponse;
import com.ctshk.common.req.pay.NotifyConsumeReq;
import com.ctshk.common.utils.*;
import com.ctshk.rpc.booking.enums.SupplierReserveTypeEnum;
import com.ctshk.rpc.booking.req.system.hotel.add.SupplierOrderSystemHotelAddReq;
import com.ctshk.rpc.booking.req.system.hotel.add.SupplierSystemOrderHotelCheckInAddReq;
import com.ctshk.rpc.booking.req.system.hotel.add.SupplierSystemOrderHotelTouristAddReq;
import com.ctshk.rpc.booking.service.ISupplierOrderService;
import com.ctshk.rpc.finance.req.bill.BillAddCollectionReq;
import com.ctshk.rpc.finance.service.IFinanceBillService;
import com.ctshk.rpc.hotel.dto.h5.HotelRoomDTO;
import com.ctshk.rpc.hotel.req.h5.RoomDetailsQueryH5Req;
import com.ctshk.rpc.order.hotel.dto.h5.HotelOrderDTO;
import com.ctshk.rpc.order.hotel.dto.h5.HotelOrderInfoDTO;
import com.ctshk.rpc.order.hotel.dto.h5.HotelOrderPointsDTO;
import com.ctshk.rpc.order.hotel.dto.h5.HotelPayDTO;
import com.ctshk.rpc.order.hotel.entity.*;
import com.ctshk.rpc.order.hotel.mapper.*;
import com.ctshk.rpc.order.hotel.req.h5.HotelCancelOrderReq;
import com.ctshk.rpc.order.hotel.req.h5.HotelOrderPointsReq;
import com.ctshk.rpc.order.hotel.req.h5.HotelOrderRoomCustomerInfoReq;
import com.ctshk.rpc.order.hotel.req.h5.HotelOrderSubmitReq;
import com.ctshk.rpc.order.hotel.service.IHotelOrderH5Service;
import com.ctshk.rpc.payment.enums.PaymentMethod;
import com.ctshk.rpc.starvip.dto.UserMemberBasicInformationDTO;
import com.ctshk.rpc.starvip.req.UserMemberBasicInformationReq;
import com.ctshk.rpc.starvip.service.IStarVipBusinessService;
import com.ctshk.rpc.system.constants.Constants;
import com.ctshk.rpc.system.dto.MainDataCurrencyListDTO;
import com.ctshk.rpc.system.dto.SysDepartmentDTO;
import com.ctshk.rpc.system.enums.CrmMemberProductPointsRateEnum;
import com.ctshk.rpc.system.req.DispatchOrderRuleGetUserReq;
import com.ctshk.rpc.system.req.MainDataCurrencyListReq;
import com.ctshk.rpc.system.req.crm.member.CrmMemberQueryReq;
import com.ctshk.rpc.system.req.crm.member.CrmMemberUpdatePointsReq;
import com.ctshk.rpc.system.service.IDispatchOrderRuleService;
import com.ctshk.rpc.system.service.IMainDataCurrencyService;
import com.ctshk.rpc.system.service.ISysDepartmentService;
import com.ctshk.rpc.system.service.ISysUserService;
import com.ctshk.rpc.user.dto.AppUserDTO;
import com.ctshk.rpc.user.service.IUserService;
import com.ctshk.rpc.workflow.dto.ApprovalStartDTO;
import com.ctshk.rpc.workflow.service.ISysApprovalService;
import com.github.houbb.heaven.util.util.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;

/**
 * <p>
 * 酒店订单表 服务实现类 C端H5
 * </p>
 *
 * @author yinhuan
 * @since 2021-03-08
 */
@Slf4j
@RefreshScope
@DubboService
public class HotelOrderH5ServiceImpl implements IHotelOrderH5Service {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private HotelOrderMapper hotelOrderMapper;
    @Autowired
    private HotelOrderTouristMapper hotelOrderTouristMapper;
    @Autowired
    private HotelOrderCollectionMapper hotelOrderCollectionMapper;
    @Autowired
    private HotelOrderReceiptMapper hotelOrderReceiptMapper;
    @Autowired
    private HotelOrderRefundMapper hotelOrderRefundMapper;

    @DubboReference
    private IStarVipBusinessService starVipBusinessService;
    @DubboReference
    private ISysUserService sysUserService;
    @DubboReference
    private IUserService userService;
    @DubboReference
    private IMainDataCurrencyService mainDataCurrencyService;
    @DubboReference
    private IDispatchOrderRuleService dispatchOrderRuleService;
    @DubboReference
    private IHotelService hotelService;
    @DubboReference
    private IHotelProductService hotelProductService;
    @DubboReference
    private IHotelH5Service hotelH5Service;
    @DubboReference
    private ISupplierOrderService supplierOrderService;
    @DubboReference
    private ISysDepartmentService sysDepartmentService;
    @DubboReference
    private IFinanceBillService financeBillService;
    @DubboReference
    private ISysApprovalService sysApprovalService;
    @DubboReference
    private IHotelRoomTypeService hotelRoomTypeService;
    @DubboReference
    private INCNBHotelService ncnbHotelService;
    @DubboReference
    private IOrderEsService orderEsService;


    @Value("${schedule.config.domain}")
    private String domain;

    @Value("${schedule.config.app-name}")
    private String appName;

    @Value("${schedule.config.password}")
    private String password;

    @Value("${schedule.job-id.order.hotel}")
    private long jobId;

    public BigDecimal getHkd(int point) {
        if (point == 0) {
            return new BigDecimal(0).setScale(0, BigDecimal.ROUND_UP);
        }
        BigDecimal exchangeRate = mainDataCurrencyService.getExchangeRate(String.valueOf(Currency.CNY.getCode()), CurrencyRateExchangeConstant.CRM_EXCHANGE_RATE_);
        BigDecimal hkd = new BigDecimal(point / 100).multiply(exchangeRate);
        return hkd.setScale(0, BigDecimal.ROUND_UP);
    }

    @Override
    public HotelOrderPointsDTO starvipPoints(HotelOrderPointsReq req, long userId) {
        HotelOrderPointsDTO result = new HotelOrderPointsDTO();
        AppUserDTO userDTO = userService.loadUserByParam(userId, 1);
        int points = CalcIntegralUtil.getObtainByType(SystemBusinessType.HOTEL.getCode(), userDTO.getMemberLevel(), req.getPrice());
        result.setReceivePoints(points);
        UserMemberBasicInformationReq informationReq = new UserMemberBasicInformationReq();
        informationReq.setPhone(userDTO.getContactNumber());
        // 获取用户当前可用星赏会积分
        Result<UserMemberBasicInformationDTO> starvipResult = starVipBusinessService.getUserMemberBasicInformation(informationReq);
        if (!starvipResult.isSuccess()) {
            throw new BusinessException(SystemError.STAR_VIP_20007);
        }
        int pointValue = Integer.valueOf(starvipResult.getData().getPoint1Value());
        result.setRemainPoints(pointValue);
        BigDecimal exchangeRate = mainDataCurrencyService.getExchangeRate(String.valueOf(Currency.CNY.getCode()), CurrencyRateExchangeConstant.CRM_EXCHANGE_RATE_);
        int usablePoints = req.getPrice().multiply(exchangeRate).divide(new BigDecimal(100)).intValue();
        // 用户当前星赏会积分与订单金额转换后的积分对比,可用积分=分数较小的一方
        if (pointValue > usablePoints) {
            result.setUsablePoints(usablePoints);
        } else {
            result.setUsablePoints(pointValue);
        }
        // 根据可用积分计算可用金额
        BigDecimal usableAmount = getHkd(result.getUsablePoints());
        result.setUsableAmount(usableAmount);
        return result;
    }

    @Override
    public HotelOrderDTO submit(HotelOrderSubmitReq req, AppReq appReq) {
        if (req.getHotelOrderRoomCustomerInfos().size() != req.getRoomNum()) {
            throw new BusinessException(SystemError.HOTEL_10081);
        }

        RoomDetailsQueryH5Req roomDetailsQueryH5Req = EntityUtil.copy(req, RoomDetailsQueryH5Req.class);
        roomDetailsQueryH5Req.setId(req.getProductId());
        HotelRoomDTO detailsRoomH5 = hotelService.detailsRoomH5(roomDetailsQueryH5Req);
        if (detailsRoomH5 == null) {
            throw new BusinessException(SystemError.HOTEL_10014);
        }

        HotelListDTO hotelDTO = hotelService.queryDetail(detailsRoomH5.getHotelId());
        if (hotelDTO == null) {
            throw new BusinessException(SystemError.HOTEL_10010);
        }

        HotelProductInfoDTO hotelProductDTO = hotelProductService.selectInfo(req.getProductId());
        if (hotelProductDTO == null) {
            throw new BusinessException(SystemError.HOTEL_10014);
        }

        LocalDateTime now = LocalDateTime.now();
        HotelOrder hotelOrder = new HotelOrder();
        Long orderId = SnowflakeIdWorker.nextId();

        Long saleManId = getSaleManId(appReq.getCompanyType());
        com.ctshk.rpc.system.dto.UserDTO userDTO = sysUserService.queryCacheUser(saleManId);

        String orderNumber = NumberUtil.generatorOrderHotelNumber(userDTO.getDeptCode(),userDTO.getEmployeeNumber());
        hotelOrder.setId(orderId);
        hotelOrder.setOrderNumber(orderNumber);
        hotelOrder.setInquiryNumber(RandomUtil.randomNumbers(6));
        hotelOrder.setOrdersType(1);
        hotelOrder.setHotelId(hotelProductDTO.getHotelId());
        hotelOrder.setRoomId(hotelProductDTO.getRoomId());
        hotelOrder.setHotelProductId(req.getProductId());
        hotelOrder.setCostCurrencyId(detailsRoomH5.getHotelProducts().get(0).getCostCurrencyId());
        hotelOrder.setCostCurrencyName(detailsRoomH5.getHotelProducts().get(0).getCostCurrencyName());
        hotelOrder.setCostExchangeRate(detailsRoomH5.getHotelProducts().get(0).getCostExchangeRate());
        hotelOrder.setCheckInDate(req.getCheckInDate());
        hotelOrder.setCheckOutDate(req.getCheckOutDate());
        hotelOrder.setNumber(req.getRoomNum());

        hotelOrder.setPaymentStatus(OrderPaymentStatus.UN_PAYMENT.getCode());
        hotelOrder.setScheduledStatus(null);
        hotelOrder.setCancelStatus(null);
        hotelOrder.setSalesmanId(saleManId);
        hotelOrder.setSalesmanName(userDTO.getFullName());
        hotelOrder.setSurname(req.getHotelOrderContactPersonInfo().getContactSurname());
        hotelOrder.setName(req.getHotelOrderContactPersonInfo().getContactName());
        hotelOrder.setContactName(StrUtil.concat(true, req.getHotelOrderContactPersonInfo().getContactSurname(), req.getHotelOrderContactPersonInfo().getContactName()));
        hotelOrder.setContactMobile(req.getHotelOrderContactPersonInfo().getContactMobile());
        hotelOrder.setContactMobileAreaCodeId(req.getHotelOrderContactPersonInfo().getContactMobileAreaCodeId());
        hotelOrder.setContactMobileAreaCode(req.getHotelOrderContactPersonInfo().getContactMobileAreaCode());
        hotelOrder.setContactCountryId(req.getHotelOrderContactPersonInfo().getContactCountryId());
        hotelOrder.setContactCountryName(req.getHotelOrderContactPersonInfo().getContactCountryName());
        hotelOrder.setContactAppellation(req.getHotelOrderContactPersonInfo().getContactAppellation());
        hotelOrder.setContactEmail(req.getHotelOrderContactPersonInfo().getContactEmail());
        hotelOrder.setNights(req.getNights());
        hotelOrder.setPayExpireDate(now.plusMinutes(30));
        hotelOrder.setMemberId(req.getMemberId());
        hotelOrder.setUsingPoints(req.getUsingPoints());
        hotelOrder.setRemark(req.getRemark());
        hotelOrder.setCreateId(saleManId);
        hotelOrder.setIsDeleted(0);
        hotelOrder.setUnitPrice(detailsRoomH5.getHotelProducts().get(0).getPrice());
        hotelOrder.setOrderAmount(buildReceivableAmount(req, detailsRoomH5.getHotelProducts().get(0).getPrice(), detailsRoomH5.getHotelProducts().get(0).getTaxation()));
        hotelOrder.setReceivableAmount(buildReceivableAmount(req, detailsRoomH5.getHotelProducts().get(0).getPrice(), detailsRoomH5.getHotelProducts().get(0).getTaxation()));
        hotelOrder.setReceiptAmount(new BigDecimal(0));
        hotelOrder.setRefundedAmount(new BigDecimal(0));
        hotelOrder.setSalesMethods(1);
        hotelOrder.setCityName(hotelDTO.getCityName());
        hotelOrder.setBrandName(hotelDTO.getBrandName());
        hotelOrder.setHotelName(hotelDTO.getName());
        hotelOrder.setRoomName(hotelProductDTO.getName());
        hotelOrder.setHotelProductName(hotelProductDTO.getName());
        hotelOrder.setHasBreakfast(hotelProductDTO.getHasBreakfast());
        hotelOrder.setCouponCode(req.getCouponCode());
        hotelOrder.setCouponDeduction(req.getCouponDeduction());
        hotelOrder.setSource(OrderSource.CPM.getCode());
        hotelOrder.setGmtCreate(now);
        hotelOrder.setGmtModified(now);
        hotelOrder.setUserId(req.getUserId());

        hotelOrderMapper.insert(hotelOrder);

        //保存旅客信息
        if(CollectionUtil.isNotEmpty(req.getHotelOrderRoomCustomerInfos())){
            for (int i = 0; i < req.getHotelOrderRoomCustomerInfos().size(); i++) {
                HotelOrderRoomCustomerInfoReq hotelOrderRoomCustomerInfo = req.getHotelOrderRoomCustomerInfos().get(i);
                HotelOrderTourist hotelOrderTourist = new HotelOrderTourist();
                hotelOrderTourist.setId(IdWorker.getId());
                hotelOrderTourist.setGmtModified(now);
                hotelOrderTourist.setCreateId(saleManId);
                hotelOrderTourist.setGender(null);
                hotelOrderTourist.setIsDeleted(0);
                hotelOrderTourist.setModifiedId(saleManId);
                hotelOrderTourist.setName(hotelOrderRoomCustomerInfo.getName());
                hotelOrderTourist.setOrderId(orderId);
                hotelOrderTourist.setSurname(hotelOrderRoomCustomerInfo.getSurName());
                hotelOrderTourist.setTouristName(hotelOrderRoomCustomerInfo.getSurName()+hotelOrderRoomCustomerInfo.getName());
                // 1:成人，2：小童  自录入的时候不区分，API可能需要
                hotelOrderTourist.setTouristType("1");
                hotelOrderTourist.setGmtCreate(now);
                hotelOrderTouristMapper.insert(hotelOrderTourist);
            }
        }

        // 星赏会积分扣除-支付成功后再扣除
        /*if (req.getUsingPoints() != null && req.getUsingPoints() > 0) {
            TransactionIntegralReq integralReq = new TransactionIntegralReq();
            integralReq.setOrderNumber(orderNumber);
            integralReq.setCtsLogin(req.getCtsLogin());
            integralReq.setPoints(new BigDecimal(req.getUsingPoints()));
            integralReq.setCtsPassword(req.getVipPwssword());
            try {
                Result starVip = starVipBusinessService.transactionIntegral(integralReq);
                if (!starVip.isSuccess()) {
                    SystemError systemError = SystemError.getDefined(starVip.getCode());
                    throw new BusinessException(systemError);
                }
            } catch (Exception e) {
                throw new BusinessException(SystemError.STAR_VIP_20006);
            }
        }*/

        /* TODO 临时注释70环境放开
        // 下单完成后调用任务执行如果未付款则超时失效订单操作
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("id", orderId);
        // 延迟时间,单位毫秒,1800000L=30分钟后执行
        long delayMS = 1800000L;
        OhMyClient taskClient = new OhMyClient(domain, appName, password);
        ResultDTO<Long> resultTask = taskClient.runJob(jobId, jsonObject.toJSONString(), delayMS);

        if (resultTask.isSuccess()) {
            // 任务执行成功,过期时间*2是防止任务启动时redis数据已过期
            redisTemplate.opsForValue().set(RedisConstants.ORDER_INSTANCELD_KEY + orderId, resultTask.getData(), delayMS * 2, TimeUnit.MILLISECONDS);
        }*/

        HotelOrderDTO result = new HotelOrderDTO();
        result.setOrderId(orderId);
        result.setProductId(hotelOrder.getHotelProductId());
        result.setOrderAmount(hotelOrder.getOrderAmount());
        result.setOrderNumber(orderNumber);
        result.setName(hotelOrder.getHotelName());
        result.setCheckInDate(req.getCheckInDate());
        result.setCheckOutDate(req.getCheckOutDate());
        result.setPayExpireDate(hotelOrder.getPayExpireDate());
        sendOrderToAppUser(orderId);
        return result;
    }

    @Override
    public CallPayDTO getCallPayInfo(long orderId) {
        HotelOrder hotelOrder = hotelOrderMapper.selectOne(Wrappers.<HotelOrder>lambdaQuery()
                .eq(HotelOrder::getId, orderId)
                .eq(HotelOrder::getPaymentStatus, OrderPaymentStatus.UN_PAYMENT.getCode())
                .eq(HotelOrder::getIsDeleted, IsDeletedCode.NO.getCode())
                .select(HotelOrder::getId, HotelOrder::getOrderNumber, HotelOrder::getReceivableAmount,
                        HotelOrder::getPayExpireDate, HotelOrder::getSalesmanId, HotelOrder::getPaymentStatus));
        if (hotelOrder == null) {
            throw new BusinessException(SystemError.AIREXPRESS_ORDER_60002);
        }
        CallPayDTO callPayDTO = EntityUtil.copy(hotelOrder, CallPayDTO.class);
        callPayDTO.setOrderValidTime(hotelOrder.getPayExpireDate());
        callPayDTO.setTotalFee(hotelOrder.getReceivableAmount());
        callPayDTO.setCurrency(PayCurrency.HKD);
        callPayDTO.setSubject("酒店订单支付");
        callPayDTO.setBody("酒店订单支付");
        return callPayDTO;
    }

    @Override
    public HotelPayDTO payment(NotifyConsumeReq notifyConsumeReq) {
        log.info("支付回调参数[{}]", notifyConsumeReq);
        String orderNumber = notifyConsumeReq.getOrderNumber();
        HotelOrder hotelOrder = hotelOrderMapper.selectOne(Wrappers.<HotelOrder>lambdaQuery()
                .eq(HotelOrder::getOrderNumber, orderNumber)
                .eq(HotelOrder::getIsDeleted, 0));
        if (hotelOrder == null) {
            //订单不存在
            throw new BusinessException(SystemError.HOTEL_10031);
        }
        LocalDateTime nowLocalDateTime = LocalDateTime.now();
        if (hotelOrder.getPaymentStatus().equals(OrderPaymentStatus.ALREADY_CANCEL.getCode()) || nowLocalDateTime.isAfter(hotelOrder.getPayExpireDate())) {
            // 支付超时
            throw new BusinessException(SystemError.HOTEL_10078);
        }
        HotelProductInfoDTO productInfoDTO = hotelProductService.selectInfo(hotelOrder.getHotelProductId());
        if (productInfoDTO == null) {
            //产品不存在
            throw new BusinessException(SystemError.HOTEL_10014);
        }
        com.ctshk.rpc.system.dto.UserDTO userDTO = sysUserService.queryCacheUser(hotelOrder.getSalesmanId());
        hotelOrder.setPaymentStatus(OrderPaymentStatus.ALREADY_PAYMENT.getCode());
        hotelOrder.setModifiedId(userDTO.getId());
        hotelOrder.setGmtModified(nowLocalDateTime);
        // 计算支付总金额和支付记录
        BigDecimal amountCountHKD = BigDecimal.ZERO;//订单金额(港币)=实收金额+积分抵扣+优惠券
        BigDecimal readyMoney = BigDecimal.ZERO;//实收金额(港币)
        PaymentMethod paymentMethod = PaymentMethod.getByCode(notifyConsumeReq.getPaymentMethod());
        HotelOrderCollection hotelOrderCollection = new HotelOrderCollection();
        hotelOrderCollection.setId(SnowflakeIdWorker.nextId());
        hotelOrderCollection.setOrderId(hotelOrder.getId());
        hotelOrderCollection.setCollectionNumber(NumberUtil.generatorPaymentNumber(nowLocalDateTime));
        hotelOrderCollection.setCurrencyId(Currency.HKD.getCode());
        hotelOrderCollection.setCurrency(Currency.HKD.getCurrency());
        // 付款方式
        hotelOrderCollection.setCollectionWayId(paymentMethod.getPayRefundMethodId());
        // 付款方式
        hotelOrderCollection.setCollectionWay(paymentMethod.getName());
        // 付款账号
        hotelOrderCollection.setPayer(notifyConsumeReq.getPaymentAccount());
        hotelOrderCollection.setCollectionAmount(notifyConsumeReq.getAmount());
        hotelOrderCollection.setStatus(OrderCollectionStatus.UN_REJECT.getCode());
        hotelOrderCollection.setSalerId(userDTO.getId());
        hotelOrderCollection.setSalerName(userDTO.getFullName());
        hotelOrderCollection.setIsDeleted(IsDeletedCode.NO.getCode());
        hotelOrderCollection.setCreateId(userDTO.getId());
        hotelOrderCollection.setGmtCreate(nowLocalDateTime);
        hotelOrderCollection.setModifiedId(userDTO.getId());
        hotelOrderCollection.setGmtModified(nowLocalDateTime);

        // 添加总实收
        hotelOrder.setReceiptAmount(hotelOrder.getReceiptAmount().add(amountCountHKD));
        hotelOrder.setPayTime(nowLocalDateTime);

        /**
         * API下单
         */
        HotelListDTO hotelListDTO = hotelService.queryDetail(hotelOrder.getHotelId());
        if (ProductType.API_PRODUCT.getCode().equals(hotelListDTO.getSourceType())) {
            HotelOrderTourist orderTourist = hotelOrderTouristMapper.selectOne(Wrappers.<HotelOrderTourist>lambdaQuery()
                    .eq(HotelOrderTourist::getOrderId, hotelOrder.getId())
                    .eq(HotelOrderTourist::getIsDeleted, 0));
            if (null == productInfoDTO) {
                throw new BusinessException(SystemError.HOTEL_10014);
            }
            if (HotelApiSupplier.NCNB.name().equals(hotelListDTO.getApiSupplier())) {
                // 龙腾捷旅API产品下单
                NCNBHotelCreateOrderReq ncnbHotelCreateOrderReq = new NCNBHotelCreateOrderReq();
                ncnbHotelCreateOrderReq.setHotelId(hotelListDTO.getHotelCode());
                ncnbHotelCreateOrderReq.setRoomId(productInfoDTO.getProductCode());
                ncnbHotelCreateOrderReq.setRatePlanId("");
                ncnbHotelCreateOrderReq.setCheckIn(hotelOrder.getCheckInDate().toString());
                ncnbHotelCreateOrderReq.setCheckOut(hotelOrder.getCheckInDate().toString());
                ncnbHotelCreateOrderReq.setRoomCount(hotelOrder.getNumber());
                ncnbHotelCreateOrderReq.setCurrency(com.ctshk.rpc.payment.enums.PayCurrency.HKD.getCode());
                ncnbHotelCreateOrderReq.setOrderAmount(String.valueOf(hotelOrder.getOrderAmount()));
                ncnbHotelCreateOrderReq.setBookName(hotelOrder.getContactName());
                ncnbHotelCreateOrderReq.setBookPhone(hotelOrder.getContactMobile());
                ncnbHotelCreateOrderReq.setGuestName(StrUtil.concat(true, orderTourist.getSurname(), orderTourist.getName()));
                ncnbHotelCreateOrderReq.setGuestPhone(hotelOrder.getContactMobile());
                ncnbHotelCreateOrderReq.setGuestFax("");
                // 013002=国籍不限
                ncnbHotelCreateOrderReq.setGuestType("013002");
                ncnbHotelCreateOrderReq.setCardTypeId("");
                ncnbHotelCreateOrderReq.setCardNum("");
                ncnbHotelCreateOrderReq.setSpecialRemark(hotelOrder.getRemark());
                ncnbHotelCreateOrderReq.setReserve1("");
                ncnbHotelCreateOrderReq.setReserve2("");
                ncnbHotelCreateOrderReq.setCustomerOrderId(hotelOrder.getOrderNumber());

                Result<BookingCheckDTO> ncnbHotelOrder = ncnbHotelService.createOrder(ncnbHotelCreateOrderReq);
                if (!ncnbHotelOrder.isSuccess()) {
                    log.error("[龙腾捷旅]酒店下单失败");
                    throw new BusinessException(SystemError.HOTEL_API_10096);
                }
                if (!"31004".equals(ncnbHotelOrder.getData().getReturnCode())) {
                    log.error("[龙腾捷旅]酒店下单失败,失败信息[{}]", ncnbHotelOrder.getData().getReturnMessage());
                    throw new BusinessException(SystemError.HOTEL_10101, ncnbHotelOrder.getData().getReturnMessage());
                }
                hotelOrder.setApiOrderNumber(ncnbHotelOrder.getData().getOrderId());

            } else if (HotelApiSupplier.WESTMINSTER.name().equals(hotelListDTO.getApiSupplier())) {
                // TODO 西敏酒店API产品下单
            }
        }

        /**
         * API下单成功后
         * 1.减去使用的会员积分，增加消费的产生会员积分
         * 2.减去使用的优惠券(机场快线无优惠券业务)
         * 3.新增会员积分、优惠券的收款记录
         */
        useAndUpdateCrmMemberPoints(hotelOrder, readyMoney, userDTO);

        // 获取当前订单下的所有旅客
        List<HotelOrderTourist> hotelOrderTourists = new ArrayList<>();
        hotelOrderTourists = hotelOrderTouristMapper.selectList(Wrappers.<HotelOrderTourist>lambdaQuery()
                .eq(HotelOrderTourist::getOrderId, hotelOrder.getId())
                .eq(HotelOrderTourist::getIsDeleted, 0));
        /**
         * 生成供应商预订单
         */
        generatorSupplierBooking(hotelOrder, hotelOrderTourists, userDTO.getId(), nowLocalDateTime);

        /**
         * 生成收据
         */
        insertReceipt(hotelOrder, userDTO.getId(), nowLocalDateTime);

        // 支付成功后流转【预订状态】
        Integer scheduledStatus = null;
        log.info("当前订单产品[{}]", productInfoDTO);
        if ("1".equals(productInfoDTO.getIsSecCheck())) {
            // 1 及时确认
            scheduledStatus = OrderScheduledStatus.RESERVE_SUCCESS.getCode();
        } else {
            // 2 二次确认
            scheduledStatus = OrderScheduledStatus.CONFIRM_RESERVE.getCode();
        }
        log.info("当前订单预订状态[{}]", scheduledStatus);
        hotelOrder.setScheduledStatus(scheduledStatus);
        hotelOrderCollectionMapper.insert(hotelOrderCollection);
        log.info("当前订单保存entity[{}]", hotelOrder);
        hotelOrderMapper.updateById(hotelOrder);

        /**
         * 财务认款
         */
        finaceCollection(Arrays.asList(hotelOrderCollection), Arrays.asList(hotelOrder.getHotelProductId()), hotelOrder.getHotelProductName());

        // 付款完成后调用取消任务执行超时失效操作
        /* TODO 临时注释70环境放开
        Long instanceId = (Long) redisTemplate.opsForValue().get(RedisConstants.ORDER_INSTANCELD_KEY + id);
        OhMyClient taskClient = new OhMyClient(domain, appName, password);
        ResultDTO<Void> resultTask = taskClient.cancelInstance(instanceId);
        if (resultTask.isSuccess()) {
            // 取消任务执行成功
            log.info("订单付款成功,取消订单失效任务操作,订单id[{}],任务实例id[{}]", id, instanceId);
        }*/
        HotelPayDTO result = new HotelPayDTO();
        result.setOrderId(hotelOrder.getId());
        result.setPayment(paymentMethod.getName());
        result.setName(hotelOrder.getHotelName());
        result.setOrderNumber(orderNumber);
        result.setInquiryNumber(hotelOrder.getInquiryNumber());
        result.setPayTime(hotelOrder.getPayTime());
        result.setIsSuccess(true);
        updateStatusToAppUser(hotelOrder.getId());
        return result;
    }

    @Override
    public HotelPayDTO paymentFinish(long id) {

        HotelOrder hotelOrder = hotelOrderMapper.selectById(id);
        if (hotelOrder == null || hotelOrder.getIsDeleted() == 1) {
            //订单不存在
            throw new BusinessException(SystemError.HOTEL_10031);
        }
        HotelOrderCollection orderCollection = hotelOrderCollectionMapper.selectOne(Wrappers.<HotelOrderCollection>lambdaQuery()
                .eq(HotelOrderCollection::getOrderId, id)
                .eq(HotelOrderCollection::getIsDeleted, 0));
        if (orderCollection == null) {
            //支付明细不存在
            throw new BusinessException(SystemError.AIR_ORDER_15019);
        }
        HotelPayDTO result = new HotelPayDTO();
        result.setOrderId(hotelOrder.getId());
        result.setPayment(orderCollection.getCollectionWay());
        result.setName(hotelOrder.getHotelName());
        result.setOrderNumber(hotelOrder.getOrderNumber());
        result.setInquiryNumber(hotelOrder.getInquiryNumber());
        result.setPayTime(hotelOrder.getPayTime());
        result.setIsSuccess(true);

        return result;
    }

    @Override
    public Long cancel(HotelCancelOrderReq req) {
        LocalDateTime now = LocalDateTime.now();
        //根据订单id查询订单信息
        HotelOrder hotelOrder = hotelOrderMapper.selectById(req.getOrderId());
        if(Objects.isNull(hotelOrder) || hotelOrder.getIsDeleted() == 1){
            throw new BusinessException(SystemError.HOTEL_10031);
        }
        // 获取订单当前状态流程
        Integer paymentStatus = hotelOrder.getPaymentStatus();
        Integer scheduledStatus = hotelOrder.getScheduledStatus();
        Integer cancelStatus = hotelOrder.getCancelStatus();

        if (paymentStatus.equals(OrderPaymentStatus.ALREADY_CANCEL.getCode()) || now.isAfter(hotelOrder.getPayExpireDate())) {
            throw new BusinessException(SystemError.HOTEL_10085);
        }
        if (ApprovalStatus.REVIEWING.getCode().equals(cancelStatus)
                || ApprovalStatus.REVIEW_YES.getCode().equals(cancelStatus)) {
            throw new BusinessException(SystemError.HOTEL_10086);
        }

        // 代付款状态可直接取消订单
        if (paymentStatus.equals(OrderPaymentStatus.UN_PAYMENT.getCode())) {
            hotelOrder.setPaymentStatus(OrderPaymentStatus.ALREADY_CANCEL.getCode());
            hotelOrder.setCancelReason(req.getCancelReason());
            hotelOrder.setCancelRemark(req.getRemark());
            hotelOrder.setGmtModified(LocalDateTime.now());
            hotelOrder.setModifiedId(hotelOrder.getSalesmanId());
            hotelOrderMapper.updateById(hotelOrder);
            updateStatusToAppUser(hotelOrder.getId());
            return hotelOrder.getId();
        }

        /**
         * 已付款状态取消订单需3步
         * 第一步：发起取消订单审批
         * 第二步：审批通过后执行财务退款
         * 第三步：财务退款完成后执行取消订单
         * 注：第二,三步不在此方法内执行
         */
        if (paymentStatus.equals(OrderPaymentStatus.ALREADY_PAYMENT.getCode())) {

            Long orderId = hotelOrder.getId();
            String cancelReason = req.getCancelReason();

            // 修改订单状态
            hotelOrder.setCancelStatus(ApprovalStatus.REVIEWING.getCode());
            hotelOrder.setCancelReason(cancelReason);
            hotelOrder.setCancelRemark(req.getRemark());
            hotelOrder.setGmtModified(now);
            hotelOrder.setModifiedId(hotelOrder.getSalesmanId());
            hotelOrderMapper.updateById(hotelOrder);
            JSONArray refundIdsJson = new JSONArray();
            // 新增退款明细
            List<HotelOrderCollection> orderCollections = hotelOrderCollectionMapper.selectList(Wrappers.<HotelOrderCollection>lambdaQuery()
                    .eq(HotelOrderCollection::getOrderId, req.getOrderId())
                    .eq(HotelOrderCollection::getIsDeleted, 0));
            List<HotelOrderRefund> hotelOrderRefunds = EntityUtil.copyList(orderCollections, HotelOrderRefund.class);
            hotelOrderRefunds.forEach(hotelOrderRefund -> {
                // 获取对应汇率
                MainDataCurrencyListDTO currencyQueryDTO = getExchangeRateByName(hotelOrderRefund.getCurrency());
                JSONObject refundIdJson = new JSONObject();
                Long refundId = SnowflakeIdWorker.nextId();
                hotelOrderRefund.setId(refundId);
                hotelOrderRefund.setExchangeRate(currencyQueryDTO.getMarketExchangeRate());
                hotelOrderRefund.setRefundReason(cancelReason);
                hotelOrderRefund.setRemark(req.getRemark());
                hotelOrderRefund.setRefundNumber(NumberUtil.generateOrderHotelRefundNumber(now));
                hotelOrderRefund.setStatus(ApprovalStatus.REVIEWING.getCode());
                hotelOrderRefund.setGmtCreate(now);
                hotelOrderRefund.setGmtModified(now);
                hotelOrderRefund.setCreateId(hotelOrder.getSalesmanId());
                hotelOrderRefund.setIsDeleted(0);

                refundIdJson.put("refundId", refundId);
                refundIdsJson.add(refundIdJson);
            });

            hotelOrderRefundMapper.insertBatchSomeColumn(hotelOrderRefunds);

            // 发起取消审批
            Result<ApprovalStartDTO> approval = sysApprovalService.start(SystemBusinessType.HOTEL.getCode(), SysApprovalType.CANCEL_HOTEL.getCode(), orderId, hotelOrder.getSalesmanId(), null, refundIdsJson.toJSONString());
            if (!approval.isSuccess()) {
                SystemError systemError = SystemError.getDefined(approval.getCode());
                throw new BusinessException(systemError);
            }
            updateStatusToAppUser(hotelOrder.getId());
            return hotelOrder.getId();
        }

        return hotelOrder.getId();
    }

    @Override
    public HotelOrderInfoDTO detail(long id) {

        HotelOrder hotelOrder = hotelOrderMapper.selectById(id);
        if(null == hotelOrder || hotelOrder.getIsDeleted() == 1) {
            throw new BusinessException(SystemError.HOTEL_10031);
        }
        HotelListDTO hotelDTO = hotelService.queryDetail(hotelOrder.getHotelId());
        Result<HotelRoomTypeDTO> roomType = hotelRoomTypeService.query(hotelOrder.getRoomId());
        if (!roomType.isSuccess()) {
            throw new BusinessException(SystemError.HOTEL_10014);
        }
        HotelProductInfoDTO productInfoDTO = hotelProductService.selectInfo(hotelOrder.getHotelProductId());
        HotelOrderCollection orderCollections = hotelOrderCollectionMapper.selectOne(Wrappers.<HotelOrderCollection>lambdaQuery()
                .eq(HotelOrderCollection::getOrderId, id)
                .eq(HotelOrderCollection::getIsDeleted, 0));
        HotelOrderInfoDTO result = EntityUtil.copy(hotelOrder, HotelOrderInfoDTO.class);
        // 构建订单状态
        buildOrderStatus(result);

        if (null != orderCollections) {
            result.setPayment(orderCollections.getCollectionWay());
        }
        result.setCustomerNotice(productInfoDTO.getCustomerNotice());
        result.setName(hotelOrder.getHotelName());
        result.setEnName(hotelDTO.getEnName());
        result.setTelephone(hotelDTO.getTelephone());
        if (null != hotelDTO.getStarLevel()) {
            result.setStarLevel(hotelDTO.getStarLevel());
            result.setStarLevelExplain(HotelStarLevel.parseValue(hotelDTO.getStarLevel()));
        }
        result.setLocation(hotelDTO.getLocation());
        result.setLon(hotelDTO.getLocation());
        result.setLat(hotelDTO.getLatitude());
        result.setProductId(hotelOrder.getHotelProductId());
        result.setCheckInWeek(DateUtil.getWeekDay(hotelOrder.getCheckInDate()));
        result.setCheckOutWeek(DateUtil.getWeekDay(hotelOrder.getCheckOutDate()));
        result.setRoomNumber(hotelOrder.getNumber());
        result.setBedType(roomType.getData().getBedType());
        result.setCustomers(Lists.newArrayList());

        // 联系人赋值
        HotelOrderContactPersonInfoDTO contactPerson = new HotelOrderContactPersonInfoDTO();
        contactPerson.setContactSurname(hotelOrder.getContactName());
        contactPerson.setContactName(hotelOrder.getContactName());
        contactPerson.setContactMobileAreaCodeId(hotelOrder.getContactMobileAreaCodeId());
        contactPerson.setContactMobileAreaCode(hotelOrder.getContactMobileAreaCode());
        contactPerson.setContactMobile(hotelOrder.getContactMobile());
        contactPerson.setContactEmail(hotelOrder.getContactEmail());
        contactPerson.setContactCountryId(hotelOrder.getContactCountryId());
        contactPerson.setContactCountryName(hotelOrder.getContactCountryName());

        result.setContactPersons(contactPerson);
        result.setReceipt("");
        result.setBookingDate(DateUtil.toLocalDate(hotelOrder.getGmtCreate()));

        return result;
    }

    /**
     * 构建订单状态
     *
     * @param hotelOrderDetailDTO
     */
    private void buildOrderStatus(HotelOrderInfoDTO hotelOrderDetailDTO) {
        if (OrderPaymentStatus.UN_PAYMENT.getCode().equals(hotelOrderDetailDTO.getPaymentStatus())) {
            if (hotelOrderDetailDTO.getScheduledStatus() == null) {
                // 待付款
                hotelOrderDetailDTO.setOrderStatus(OrderStatus.UN_PAYMENT.getCode());
            }
        } else if (Objects.equals(OrderPaymentStatus.ALREADY_PAYMENT.getCode(), hotelOrderDetailDTO.getPaymentStatus())) {
            if (Objects.equals(hotelOrderDetailDTO.getScheduledStatus(), OrderScheduledStatus.CONFIRM_RESERVE.getCode())) {
                // 待预订
                hotelOrderDetailDTO.setOrderStatus(OrderStatus.CONFIRM_RESERVE.getCode());
            } else if (Objects.equals(OrderScheduledStatus.RESERVE_SUCCESS.getCode(), hotelOrderDetailDTO.getScheduledStatus())) {
                // 确认成功
                hotelOrderDetailDTO.setOrderStatus(OrderStatus.RESERVE_SUCCESS.getCode());
            } else {
                // 确认失败
                hotelOrderDetailDTO.setOrderStatus(OrderStatus.RESERVE_FAIL.getCode());
            }
        } else if (OrderPaymentStatus.ALREADY_CANCEL.getCode().equals(hotelOrderDetailDTO.getPaymentStatus())) {
            // 已取消
            hotelOrderDetailDTO.setOrderStatus(OrderStatus.ALREADY_CANCEL.getCode());
        } else {
            // 确认失败
            hotelOrderDetailDTO.setOrderStatus(OrderStatus.RESERVE_FAIL.getCode());
        }
    }

    /**
     * 获取销售人并生成销售编号
     *
     * @param companyType
     */
    private Long getSaleManId(String companyType) {
        DispatchOrderRuleGetUserReq dispatchOrderRuleGetUserReq = new DispatchOrderRuleGetUserReq();
        dispatchOrderRuleGetUserReq.setBusinessTypeId(SystemBusinessType.AIR_EXPRESS.getCode());
        dispatchOrderRuleGetUserReq.setCompanyType(companyType);
        Result<Long> result = dispatchOrderRuleService.getDispatchUserId(dispatchOrderRuleGetUserReq);
        if (!result.isSuccess()) {
            log.error("【酒店APP-提交订单】获取销售人失败:{},msg:{}", result.getCode(), result.getMsg());
            throw new BusinessException(SystemError.getDefined(result.getCode()));
        }
        return result.getData();
    }

    /**
     * 根据总价计算应收金额，减去会员积分和优惠券
     * @param req
     * @return
     */
    private BigDecimal buildReceivableAmount(HotelOrderSubmitReq req, BigDecimal unitPrice, BigDecimal taxation) {

        // 计算总价=(房间单价+服务费)*房间数*住宿晚数
        // BigDecimal receivableAmount = unitPrice.add(taxation).multiply(new BigDecimal(req.getRoomNum())).multiply(new BigDecimal(req.getNights()));
        // 这里单价和税费已经在获取酒店产品时计算好,这里直接进行相加就行
        BigDecimal receivableAmount = unitPrice.add(taxation);
        //计算应收价格-是否使用积分
        if (req.getUsingPoints() != null && req.getUsingPoints() != 0) {
            if (req.getUsingPoints() < 0) {
                // 抵扣积分必须大于0;
                throw new BusinessException(SystemError.HOTEL_10074);
            }
            BigDecimal usingPoints = new BigDecimal(String.valueOf(req.getUsingPoints()));

            //计算应收价格-计算积分
            BigDecimal usingPointsCNY = usingPoints.divide(Constants.POINTS_TO_CNY, 0, BigDecimal.ROUND_UP);
            //人民币兑港币
            MainDataCurrencyListDTO exchangeRateByName = getExchangeRateByName(Currency.CNY.getSymbol());
            BigDecimal crmExchangeRate = exchangeRateByName.getCrmExchangeRate();
            BigDecimal usingPointsHKD = usingPointsCNY.multiply(crmExchangeRate);
            receivableAmount = receivableAmount.subtract(usingPointsHKD);
        }
        //计算应收价格-计算推广码（优惠券）
        if (StringUtils.isNotBlank(req.getCouponCode())) {
            receivableAmount = receivableAmount.subtract(req.getCouponDeduction());
        }
        if (receivableAmount.compareTo(BigDecimal.ZERO) == -1) {
            receivableAmount = BigDecimal.ZERO;
            // 总价减去积分价后为负数，不予许提交
            // throw new BusinessException(SystemError.AIREXPRESS_ORDER_60007);
        }
        return receivableAmount;
    }

    /**
     * 根据币种名称获取汇率
     *
     * @param currencySy
     * @return
     */
    private MainDataCurrencyListDTO getExchangeRateByName(String currencySy) {
        MainDataCurrencyListReq mainDataCurrencyListReq = new MainDataCurrencyListReq();
        mainDataCurrencyListReq.setName(currencySy);
        PageResponse<MainDataCurrencyListDTO> list = mainDataCurrencyService.list(mainDataCurrencyListReq);
        if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(list.getRecords())) {
            throw new BusinessException(SystemError.AIREXPRESS_ORDER_60003);
        }
        return list.getRecords().get(0);
    }

    /**
     * 使用和更新crm会员积分
     *
     * @param hotelOrder
     * @param readyMoney                             实收金额(现金，信用卡)
     * @param user
     */
    private void useAndUpdateCrmMemberPoints(HotelOrder hotelOrder, BigDecimal
            readyMoney, com.ctshk.rpc.system.dto.UserDTO user) {
        // 更新CRM会员积分记录
        log.info("【机场快线订单】更新会员信息,使用积分:{}, 查询类型:{},查询号:{},证书类型:{}", hotelOrder.getUsingPoints(), hotelOrder.getMemberQueryType(), hotelOrder.getMemberQueryCode(), hotelOrder.getMemberQueryCertificatesType());
        if (hotelOrder.getUsingPoints() != null && hotelOrder.getUsingPoints() > 0) {
            // 获取人民币兑港币汇率
            MainDataCurrencyListDTO mainDataCurrencyListDTO = getExchangeRateByName(Currency.CNY.getCurrency());
            BigDecimal crmExchangeRateCNY = mainDataCurrencyListDTO.getCrmExchangeRate();
            CrmMemberQueryReq crmMemberQueryReq = new CrmMemberQueryReq(hotelOrder.getMemberQueryType(), hotelOrder.getMemberQueryCode(), hotelOrder.getMemberQueryCertificatesType().toString());
            try {
                // TODO ContactQueryDTO contactQuery = crmMemberService.queryMember(crmMemberQueryReq);
                // 构建创建用户ID，人民币金额，加分点数
                String crmUserId = String.valueOf(user.getId()).substring(0, 12);
                BigDecimal rmbAmount = readyMoney.divide(crmExchangeRateCNY, 2, BigDecimal.ROUND_HALF_UP);
                BigDecimal addPoints = readyMoney.divide(new BigDecimal(String.valueOf(CrmMemberProductPointsRateEnum.TRAFFIC_TYPE.getRate())), BigDecimal.ROUND_DOWN);
                CrmMemberUpdatePointsReq crmMemberUpdatePointsReq = new CrmMemberUpdatePointsReq();
                crmMemberUpdatePointsReq.setUpdateType(2);
                crmMemberUpdatePointsReq.setNumPoints(new BigDecimal(hotelOrder.getUsingPoints()));//do积分点数
                crmMemberUpdatePointsReq.setXCtsProdDesc("單項委託 – 酒店");//do板塊產品描述 (建議使用: 集團產品名稱 + “ – “ + 板塊產品名稱, 例如: 港澳参团游 - 主题游)")
                crmMemberUpdatePointsReq.setTxnNum(hotelOrder.getOrderNumber() + "-1");//do交易編號 (如為旅行團交易, 目前我們的格式是 旅行團交易編號 + ‘-‘ + 序號, 例如: 01P08192131025A155506-1
                crmMemberUpdatePointsReq.setAmtVal(readyMoney);
                crmMemberUpdatePointsReq.setXCtsRmbAmount(rmbAmount);
                crmMemberUpdatePointsReq.setCreateUserid(crmUserId);
                crmMemberUpdatePointsReq.setXCtsTxnOrgCode("219910000010");
                crmMemberUpdatePointsReq.setTxnChannelCd("16");
                crmMemberUpdatePointsReq.setXCtsTxnChnNum("211099000010");
                crmMemberUpdatePointsReq.setProdName("8821090000");
                crmMemberUpdatePointsReq.setXCtsProdCode("8821090300");
                // TODO crmMemberService.updatePoints(crmMemberUpdatePointsReq, contactQuery);
                // 新增-加积分记录
                if (readyMoney.compareTo(BigDecimal.ZERO) == -1) {
                    // 实收金额不能小于0
                    throw new BusinessException(SystemError.AIREXPRESS_ORDER_60020);
                }
                CrmMemberUpdatePointsReq crmMemberIncreasePointsReq = new CrmMemberUpdatePointsReq();
                crmMemberIncreasePointsReq.setUpdateType(1);
                crmMemberIncreasePointsReq.setNumPoints(addPoints);//do积分点数
                crmMemberIncreasePointsReq.setXCtsProdDesc("單項委託-火车巴士船票(机场快线)");//do板塊產品描述 (建議使用: 集團產品名稱 + “ – “ + 板塊產品名稱, 例如: 港澳参团游 - 主题游)")
                crmMemberIncreasePointsReq.setTxnNum(hotelOrder.getOrderNumber() + "-2");//do交易編號 (如為旅行團交易, 目前我們的格式是 旅行團交易編號 + ‘-‘ + 序號, 例如: 01P08192131025A155506-1
                crmMemberIncreasePointsReq.setAmtVal(readyMoney);
                crmMemberIncreasePointsReq.setXCtsRmbAmount(rmbAmount);
                crmMemberIncreasePointsReq.setCreateUserid(crmUserId);
                crmMemberIncreasePointsReq.setXCtsTxnOrgCode("219910000010");
                crmMemberIncreasePointsReq.setTxnChannelCd("16");
                crmMemberIncreasePointsReq.setXCtsTxnChnNum("211099000010");
                crmMemberIncreasePointsReq.setProdName("8821090000");
                crmMemberIncreasePointsReq.setXCtsProdCode("8821090300");
                // TODO crmMemberService.updatePoints(crmMemberIncreasePointsReq, contactQuery);
                // 新增CRM会员付款方式
                HotelOrderCollection hotelOrderCollection = new HotelOrderCollection();
                hotelOrderCollection.setId(SnowflakeIdWorker.nextId());
                hotelOrderCollection.setOrderId(hotelOrder.getId());
                hotelOrderCollection.setCollectionAmount(readyMoney);
                hotelOrderCollection.setStandardAmount(rmbAmount);
                // 支付方式：CRM星賞會積分
                hotelOrderCollection.setCollectionWayId(PayRefundMethod.CRM.getId());
                hotelOrderCollection.setCollectionWay(PayRefundMethod.CRM.getName());
                hotelOrderCollection.setIsDeleted(IsDeletedCode.NO.getCode());
                hotelOrderCollection.setCreateId(user.getId());
                hotelOrderCollection.setGmtCreate(hotelOrder.getGmtModified());
                hotelOrderCollection.setModifiedId(user.getId());
                hotelOrderCollection.setGmtModified(hotelOrder.getGmtModified());
            } catch (Exception e) {
                if (e instanceof BusinessException) {
                    throw e;
                } else {
                    throw new BusinessException(SystemError.AIREXPRESS_ORDER_60009);
                }
            }
        }
    }

    /**
     *  生成供应商预订单
     * @param hotelOrder
     * @param hotelOrderTourists
     * @param userId
     * @param localDateTime
     */
    private void generatorSupplierBooking(HotelOrder hotelOrder, List<HotelOrderTourist> hotelOrderTourists, Long userId, LocalDateTime localDateTime) {
        SupplierOrderSystemHotelAddReq supplierOrderSystemHotelAddReq = new SupplierOrderSystemHotelAddReq();
        log.info("【酒店订单】生成供应商预订，查询酒店产品信息, id:{}", hotelOrder.getHotelProductId());
        HotelProductInfoDTO hotelProductInfoDTO = hotelProductService.selectInfo(hotelOrder.getHotelProductId());
        log.info("【酒店订单】生成供应商预订，酒店产品信息, info:{}", JSON.toJSONString(hotelProductInfoDTO));
        // 生成供应预订单
        com.ctshk.rpc.system.dto.UserDTO chargeUser = sysUserService.queryCacheUser(hotelProductInfoDTO.getChargeId());
        // 资源所属部门ID
        supplierOrderSystemHotelAddReq.setDepartmentId(chargeUser.getDeptId());
        supplierOrderSystemHotelAddReq.setDepartment(chargeUser.getDept());
        supplierOrderSystemHotelAddReq.setBusinessType(SystemBusinessType.HOTEL.getCode());
        supplierOrderSystemHotelAddReq.setReserveTypeId(SupplierReserveTypeEnum.HOTEL.getId());
        supplierOrderSystemHotelAddReq.setSaleType(SalesMethod.SINGLE_SALE.getCode());
        if (hotelOrder.getIsManual() == 1) {
            supplierOrderSystemHotelAddReq.setProductType(ProductType.PLUG.getCode());
        } else {
            supplierOrderSystemHotelAddReq.setProductType(hotelProductInfoDTO.getSourceType());
        }
        supplierOrderSystemHotelAddReq.setTotalFee(hotelOrder.getOrderAmount());
        supplierOrderSystemHotelAddReq.setPayableAmount(hotelOrder.getReceivableAmount());
        supplierOrderSystemHotelAddReq.setSaleNumber(hotelOrder.getOrderNumber());
        supplierOrderSystemHotelAddReq.setProductNumber(hotelProductInfoDTO.getProductCode());
        supplierOrderSystemHotelAddReq.setProductName(hotelProductInfoDTO.getName());
        supplierOrderSystemHotelAddReq.setCurrencyId(hotelOrder.getCostCurrencyId());
        supplierOrderSystemHotelAddReq.setCurrency(hotelOrder.getCostCurrencyName());
        // 供应商ID
        supplierOrderSystemHotelAddReq.setSupplierId(hotelProductInfoDTO.getSupplierId());
        // 供应商订单类型
        supplierOrderSystemHotelAddReq.setSupplierOrderType("1");
        // 供应商订单号(第三方单号)
        supplierOrderSystemHotelAddReq.setSupplierOrderNumber("");
        // 确认状态
        supplierOrderSystemHotelAddReq.setConfirmStatus(SupplierConfirmStatus.CONFIRMED.getCode());
        supplierOrderSystemHotelAddReq.setConfirmTime(localDateTime);

        // 构建供应商预订-酒店入住信息
        SupplierSystemOrderHotelCheckInAddReq supplierSystemOrderHotelCheckInAddReq = new SupplierSystemOrderHotelCheckInAddReq();
        supplierSystemOrderHotelCheckInAddReq.setHotelId(hotelProductInfoDTO.getHotelId());
        supplierSystemOrderHotelCheckInAddReq.setHotelName(hotelOrder.getHotelName());
        supplierSystemOrderHotelCheckInAddReq.setProductId(hotelProductInfoDTO.getId());
        supplierSystemOrderHotelCheckInAddReq.setProductName(hotelProductInfoDTO.getName());
        supplierSystemOrderHotelCheckInAddReq.setIncludeBreakfast(hotelProductInfoDTO.getHasBreakfast());
        supplierSystemOrderHotelCheckInAddReq.setCheckInTime(DateUtil.toLocalDateTime(hotelOrder.getCheckInDate()));
        supplierSystemOrderHotelCheckInAddReq.setCheckOutTime(DateUtil.toLocalDateTime(hotelOrder.getCheckOutDate()));
        supplierSystemOrderHotelCheckInAddReq.setNum(hotelOrder.getNumber());
        supplierSystemOrderHotelCheckInAddReq.setNightNum(hotelOrder.getNights());
        // 房间单价-无需计算服务费和加幅
        supplierSystemOrderHotelCheckInAddReq.setPrice(hotelOrder.getReceivableAmount());
        supplierSystemOrderHotelCheckInAddReq.setRoomTypeId(hotelProductInfoDTO.getRoomId());
        supplierSystemOrderHotelCheckInAddReq.setRoomType(hotelOrder.getRoomName());
        // 小计-无需计算服务费和加幅
        supplierSystemOrderHotelCheckInAddReq.setTotalPrice(hotelOrder.getReceivableAmount());
        supplierSystemOrderHotelCheckInAddReq.setExtraService("");
        supplierSystemOrderHotelCheckInAddReq.setSuitablePeople(hotelProductInfoDTO.getTargetPopulation());
        supplierSystemOrderHotelCheckInAddReq.setCustomerRemark(hotelOrder.getRemark());
        supplierSystemOrderHotelCheckInAddReq.setSupplierRemark(hotelOrder.getRemark());

        // 构建供应商预订-酒店入住游客
        List<SupplierSystemOrderHotelTouristAddReq> supplierSystemOrderAirExpressDetailAddReqList = new ArrayList<>();
        Optional.ofNullable(hotelOrderTourists).orElse(new ArrayList<>()).forEach(hotelOrderTourist -> {
            SupplierSystemOrderHotelTouristAddReq supplierSystemOrderHotelTouristAddReq = new SupplierSystemOrderHotelTouristAddReq();
            supplierSystemOrderHotelTouristAddReq.setTouristId(hotelOrderTourist.getId());
            supplierSystemOrderHotelTouristAddReq.setTouristName(hotelOrderTourist.getTouristName());
            supplierSystemOrderHotelTouristAddReq.setTouristTypeId(Long.valueOf(hotelOrderTourist.getTouristType()));
            supplierSystemOrderHotelTouristAddReq.setTouristType(TouristType.parseValue(Integer.valueOf(hotelOrderTourist.getTouristType())));
            supplierSystemOrderHotelTouristAddReq.setContactMobile(hotelOrder.getContactMobile());
            supplierSystemOrderHotelTouristAddReq.setContactAreaCode(hotelOrder.getContactMobileAreaCode());

            supplierSystemOrderAirExpressDetailAddReqList.add(supplierSystemOrderHotelTouristAddReq);
        });
        supplierSystemOrderHotelCheckInAddReq.setSupplierSystemOrderHotelTouristAddReqList(supplierSystemOrderAirExpressDetailAddReqList);


        List<SupplierSystemOrderHotelCheckInAddReq> supplierSystemOrderHotelCheckInAddReqs = new ArrayList<>();
        supplierSystemOrderHotelCheckInAddReqs.add(supplierSystemOrderHotelCheckInAddReq);
        supplierOrderSystemHotelAddReq.setSupplierSystemOrderHotelCheckInAddReqList(supplierSystemOrderHotelCheckInAddReqs);
        supplierOrderService.addSystemHotel(supplierOrderSystemHotelAddReq, userId);
    }

    /**
     * 付款后生成收据
     * @param hotelOrder
     * @param userId
     * @param nowLocalDateTime
     * @return
     */
    private int insertReceipt(HotelOrder hotelOrder, Long userId, LocalDateTime nowLocalDateTime) {
        com.ctshk.rpc.system.dto.UserDTO userDTO = sysUserService.loadUserByParam(userId, 1);
        SysDepartmentDTO sysDepartmentDTO = sysDepartmentService.queryOne(userDTO.getDeptId());
        //生成收据单号
        String receiptNumber = NumberUtil.generatorReceiptNumber(userDTO.getDeptCode(), userDTO.getEmployeeNumber(), nowLocalDateTime);
        HotelOrderReceipt hotelOrderReceipt = new HotelOrderReceipt();
        hotelOrderReceipt.setId(SnowflakeIdWorker.nextId());
        hotelOrderReceipt.setOrderId(hotelOrder.getId());
        hotelOrderReceipt.setReceiptNo(receiptNumber);
        hotelOrderReceipt.setBranch(sysDepartmentDTO.getTitle());
        hotelOrderReceipt.setGuest(hotelOrder.getContactName());
        hotelOrderReceipt.setRoomNumber(String.valueOf(hotelOrder.getNumber()));
        hotelOrderReceipt.setPhone(hotelOrder.getContactMobileAreaCode() + "-" + hotelOrder.getContactMobile());
        hotelOrderReceipt.setIsDeleted(IsDeletedCode.NO.getCode());
        hotelOrderReceipt.setCreateId(userId);
        hotelOrderReceipt.setGmtCreate(nowLocalDateTime);
        hotelOrderReceipt.setModifiedId(userId);
        hotelOrderReceipt.setGmtModified(nowLocalDateTime);
        return hotelOrderReceiptMapper.insert(hotelOrderReceipt);
    }

    /**
     * 收款新增财务中心认款
     * @param hotelOrderCollections
     * @param productIds
     * @param productName
     * @return
     */
    private void finaceCollection(List<HotelOrderCollection> hotelOrderCollections, List<Long> productIds, String productName) {
        List<BillAddCollectionReq> req = new ArrayList<>();
        hotelOrderCollections.forEach(hotelOrderCollection -> {
            BillAddCollectionReq billAddCollectionReq = new BillAddCollectionReq();
            billAddCollectionReq.setBillNumber(hotelOrderCollection.getCollectionNumber());
            billAddCollectionReq.setBusinessType(SystemBusinessType.HOTEL.getCode());
            billAddCollectionReq.setCollectionWayId(hotelOrderCollection.getCollectionWayId());
            billAddCollectionReq.setCollectionWayName(hotelOrderCollection.getCollectionWay());
            billAddCollectionReq.setCollectionAmount(hotelOrderCollection.getCollectionAmount());
            billAddCollectionReq.setCurrencyId(hotelOrderCollection.getCurrencyId());
            billAddCollectionReq.setCurrencyName(hotelOrderCollection.getCurrency());
            billAddCollectionReq.setExchangeRate(hotelOrderCollection.getExchangeRate().doubleValue());
            billAddCollectionReq.setCollectionTime(hotelOrderCollection.getGmtCreate());
            billAddCollectionReq.setProductId(productIds);
            billAddCollectionReq.setProductName(productName);
            billAddCollectionReq.setSaleUserId(hotelOrderCollection.getSalerId());
            billAddCollectionReq.setSaleUserName(hotelOrderCollection.getSalerName());
            billAddCollectionReq.setSaleDeptId(sysUserService.queryCacheUser(hotelOrderCollection.getSalerId()).getDeptId());
            billAddCollectionReq.setUserId(hotelOrderCollection.getCreateId());
            req.add(billAddCollectionReq);
        });
        log.info("发起收款新增财务中心认款开始");
        Result result = financeBillService.addCollection(req);
        if (!result.isSuccess()) {
            SystemError systemError = SystemError.getDefined(result.getCode());
            throw new BusinessException(systemError);
        }
        log.info("发起收款新增财务中心认款成功");
    }

    public MyHotelOrderDTO buildEsOrderDTO(Long orderId) {
        HotelOrder order = hotelOrderMapper.selectById(orderId);
        MyHotelOrderDTO orderDTO = EntityUtil.copy(order, MyHotelOrderDTO.class);
        HotelListDTO hotelListDTO = hotelService.queryDetail(orderDTO.getHotelId());
        orderDTO.setHotelEnName(hotelListDTO.getEnName());
        orderDTO.setHotelName(hotelListDTO.getName());
        return orderDTO;
    }

    /**
     * 异步发送订单数据到个人中心
     * @param orderId 订单Id
     */
    public void sendOrderToAppUser(Long orderId) {
        CompletableFuture.runAsync(() -> {
            try {
                MyHotelOrderDTO orderDTO = buildEsOrderDTO(orderId);
                orderEsService.saveOrderToEs(orderDTO, SystemBusinessType.HOTEL);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 异步更新订单状态到个人中心
     */
    public void updateStatusToAppUser(Long orderId) {
        CompletableFuture.runAsync(() -> {
            try {
                HotelOrder order = hotelOrderMapper.selectById(orderId);
                orderEsService.updateStatusToEs(order.getId(),SystemBusinessType.HOTEL.getCode(),
                        null, order.getPaymentStatus(), order.getScheduledStatus(),
                        order.getCancelStatus());
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
}
